/**
 * This class embodies a project and affecting commands in the UI.
 */
package org.hackystat.ui.projectviewer.client;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map.Entry;

import org.hackystat.ui.projectviewer.client.common.ProjectParcel;

import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.WindowResizeListener;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * This class embodies a project and affecting commands in the UI.
 * @author David J. Nickles
 *
 */
public class ProjectPanel extends TreeItem implements ClickListener, WindowResizeListener {

  private ProjectParcel projectParcel;
  private Session session;
  private Button ownerEditButton;
  private Button memberEditButton;
  private Button ownerDeleteButton;
  private Button acceptButton;
  private Button declineButton;
  private Image projectIcon;
  private Label feedback;
  private VerticalPanel headerFeedbackPanel;
  private HorizontalPanel actionstrip;
  private HorizontalPanel row1col2header;

  /** {@literal} DEFAULTPROJECT stylesheet class for examples. */  
  private static final String DEFAULTPROJECT = "Default"; 
  
  /**
   * Constructs a new ProjectsList and populates it from the current session.
   * @param parcel the project parcel
   * @param session the session
   */
  public ProjectPanel(ProjectParcel parcel, Session session) {
    this.projectParcel = parcel;
    this.session = session;
    this.projectIcon = new Image(Images.WAIT_IMAGE);
    Label userPanelFeedback = new Label("Owner: " + parcel.getOwner());
    this.actionstrip = new HorizontalPanel();
    if (parcel.getOwner().equals(session.getUser())) {
      this.projectIcon.setUrl(Images.OWNERIMAGE);
      userPanelFeedback.setStyleName(Styles.OWNERLABELSTYLE);
      if (!DEFAULTPROJECT.equals(parcel.getName())) {
        this.ownerEditButton = new Button("Edit");
        actionstrip.add(this.ownerEditButton);
        this.ownerEditButton.addClickListener(this);
        this.ownerDeleteButton = new Button("Delete");
        actionstrip.add(this.ownerDeleteButton);
        this.ownerDeleteButton.addClickListener(this);
      }
    } 
    else if (parcel.getMembers().contains(session.getUser())) {
      this.projectIcon.setUrl(Images.MEMBERIMAGE);
      userPanelFeedback.setStyleName(Styles.EXAMPLELABELSTYLE);
      this.memberEditButton = new Button("Leave");
      this.memberEditButton.setTitle("Remove yourself as a member of this project.");
      actionstrip.add(this.memberEditButton);
      this.memberEditButton.addClickListener(this);
    }
    else if (parcel.getInvitations().contains(session.getUser())) {
      this.projectIcon.setUrl(Images.INVITATIONIMAGE);
      userPanelFeedback.setStyleName(Styles.EXAMPLELABELSTYLE);
      this.acceptButton = new Button("Accept");
      actionstrip.add(this.acceptButton);
      this.acceptButton.addClickListener(this);
      this.declineButton = new Button("Decline");
      actionstrip.add(this.declineButton);
      this.declineButton.addClickListener(this);
    }
    HorizontalPanel hp = new HorizontalPanel();
    hp.add(this.projectIcon);
    
    this.headerFeedbackPanel = new VerticalPanel();
    
    this.row1col2header = new HorizontalPanel();
    row1col2header.setWidth(Integer.toString(Window.getClientWidth() - 114) + Styles.PIXELSUFFIX);
    Label userPanelTitle = new Label(parcel.getName());
    userPanelTitle.setStyleName(Styles.MEDIUMLABELSTYLE);
    row1col2header.add(userPanelTitle);
    row1col2header.setCellHorizontalAlignment(userPanelTitle, HasHorizontalAlignment.ALIGN_LEFT);
    row1col2header.add(actionstrip);
    row1col2header.setCellHorizontalAlignment(actionstrip, HasHorizontalAlignment.ALIGN_RIGHT);
    headerFeedbackPanel.add(row1col2header);
    headerFeedbackPanel.add(userPanelFeedback);
    Label timespanFeedback = new Label("Span: " + parcel.getStartTime() 
      + " to " + parcel.getEndTime());
    timespanFeedback.setStyleName(Styles.EXAMPLELABELSTYLE);
    headerFeedbackPanel.add(timespanFeedback);
    this.feedback = new Label();
    headerFeedbackPanel.add(this.feedback);
    
    hp.add(headerFeedbackPanel);
    hp.setTitle(parcel.getDescription());

    setWidget(hp);
    Window.addWindowResizeListener(this);
    
    addItem("Description: " + parcel.getDescription());
    TreeItem membersRoot = new TreeItem("Members");
    addItem(membersRoot);
    Iterator membersIt = parcel.getMembers().listIterator();
    while (membersIt.hasNext()) {
      membersRoot.addItem((String)membersIt.next());
    }
    membersRoot.setState(true);
    TreeItem invitationsRoot = new TreeItem("Invitations");
    addItem(invitationsRoot);
    Iterator invitationsIt = parcel.getInvitations().listIterator();
    while (invitationsIt.hasNext()) {
      invitationsRoot.addItem((String)invitationsIt.next());
    }
    invitationsRoot.setState(true);
    TreeItem propertiesRoot = new TreeItem("Properties");
    addItem(propertiesRoot);
    Iterator propertyIt = parcel.getProperties().entrySet().iterator();
    while (propertyIt.hasNext()) {
      Entry entry = (Entry)propertyIt.next();
      propertiesRoot.addItem((String)entry.getKey() + " : " 
        + (String)entry.getValue());
    }
    propertiesRoot.setState(true);
    TreeItem uriPatternsRoot = new TreeItem("URI Patterns");
    addItem(uriPatternsRoot);
    Iterator uriPatternsIt = parcel.getUriPatterns().listIterator();
    while (uriPatternsIt.hasNext()) {
      uriPatternsRoot.addItem((String)uriPatternsIt.next());
    }
    uriPatternsRoot.setState(true);
  }

  /** 
   * Listens for clicks on the UI elements.
   * @param sender the UI widget that is losing focus.
   */
  public void onClick(Widget sender) {
    if (sender.equals(this.ownerEditButton)) {
      RootPanel.get(CompositeWidget.PROJECTEDIT_ELEMENT).add(
        new ProjectEditPanel(this.session, this.projectParcel));
      RootPanel.get(CompositeWidget.PROJECTVIEWER_ELEMENT).remove(0);
    } 
    else if (sender.equals(this.ownerDeleteButton)) {
      final Widget temp = this.getWidget();
      VerticalPanel vp = new VerticalPanel();
      vp.add(new Label("Are you sure that you want to delete the project "
        + this.projectParcel.getName() + " and all of its associated data?"));
      
      final Button ok = new Button("Delete");
      final Button cancel = new Button("Cancel");
      ClickListener listener = new ClickListener() {
        public void onClick(Widget sender) {
          if (sender.equals(ok)) {
            rpcDelete();
          }
          else {
            setWidget(temp);
          }
        }
      };
      ok.addClickListener(listener);
      cancel.addClickListener(listener);
      vp.add(ok);
      vp.add(cancel);
      setWidget(vp); 
    }
    else if (sender.equals(this.memberEditButton)) {
      rpcRSVP(Boolean.FALSE);
      rpcGetProjectParcels();
    }
    else if (sender.equals(this.acceptButton)) {
      rpcRSVP(Boolean.TRUE);
      rpcGetProjectParcels();
    } 
    else if (sender.equals(this.declineButton)) {
      rpcRSVP(Boolean.FALSE);
      rpcGetProjectParcels();
    }
  }
  
  /**
   * This method sets the state of the host uri validation.
   * @param state Boolean represents the state of the user valid=true, invalid=false
   */
  public void setIvitationStatus(String state) {
    if (Boolean.TRUE.toString().equals(state)) {
      this.feedback.setText("Successfully responded to invitation.");
      this.feedback.setStyleName(Styles.VALIDLABELSTYLE);
      this.projectIcon.setUrl(Images.MEMBERIMAGE);
      this.actionstrip.clear();
      this.actionstrip.add(this.memberEditButton = new Button("Edit"));
      this.memberEditButton.addClickListener(this);
    }
    else {
      this.projectIcon.setUrl(Images.INVITATIONIMAGE);
      this.feedback.setText(state);
      this.feedback.setStyleName(Styles.INVALIDLABELSTYLE);
    }
  }
  
  /**
   * Respond to invitation from specified owner and project.
   * @param response true if accepting, false if declining.
   */
  public void rpcRSVP (Boolean response) {
    this.projectIcon.setUrl(Images.WAIT_IMAGE);
    RPCServiceFactory.getInstance().getService().rsvp(session.getSensorbaseHost(), 
      session.getUser(), session.getPassword(), projectParcel.getName(), projectParcel.getOwner(), 
      response, new AsyncCallback()
      {
        public void onSuccess(Object result) {
          setIvitationStatus((String) result);
        }

        public void onFailure(Throwable caught) {
          if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
            CompositeWidget.log(RPCServiceFactory.PRCERRORNAME, 
              "Cannot invoke user validation on servlet.");
          } 
          else {
            CompositeWidget.log(RPCServiceFactory.PRCERRORNAME, caught.toString());
          }
        }
      });
  }  
  
  /**
   * This method sets the state of the host uri validation.
   * @param state Boolean represents the state of the user valid=true, invalid=false
   */
  public void processDeleteResult(String state) {
    if (Boolean.TRUE.toString().equals(state)) {
      this.feedback.setText("Successfully deleted project.");
      this.feedback.setStyleName(Styles.VALIDLABELSTYLE);
      this.projectIcon.setUrl(Images.MEMBERIMAGE);
      remove();
    }
    else {
      this.projectIcon.setUrl(Images.MEMBERIMAGE);
      this.feedback.setText(state);
      //this.feedback.setText("Unable to respond to invitation.");
      this.feedback.setStyleName(Styles.INVALIDLABELSTYLE);
    }
  }
  
  /**
   * Respond to invitation from specified owner and project.
   */
  public void rpcDelete () {
    this.projectIcon.setUrl(Images.WAIT_IMAGE);
    RPCServiceFactory.getInstance().getService().deleteProject(session.getSensorbaseHost(), 
      session.getUser(), session.getPassword(), projectParcel.getName(), new AsyncCallback()
      {
        public void onSuccess(Object result) {
          processDeleteResult((String) result);
        }

        public void onFailure(Throwable caught) {
          if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
            CompositeWidget.log(RPCServiceFactory.PRCERRORNAME, 
              "Cannot invoke user validation on servlet.");
          } 
          else {
            CompositeWidget.log(RPCServiceFactory.PRCERRORNAME, caught.toString());
          }
        }
      });
  }  
  
  /**
   * This method sets the state of the user & key validation.
   * @param projects the list of projects for this user.
   * 
   * @gwt.typeArgs projects <java.util.ArrayList<java.lang.String>>
   */
  private void setUserProjects(ArrayList projects) {
    this.session.setProjects(projects);
    RootPanel.get(CompositeWidget.PROJECTVIEWER_ELEMENT).clear();
    RootPanel.get(CompositeWidget.PROJECTVIEWER_ELEMENT).add(new ProjectListPanel(this.session));
  }
  
  /**
   * Check with the servlet to see if the user/password is valid.
   */
  private void rpcGetProjectParcels() {
    this.projectIcon.setUrl(Images.WAIT_IMAGE);
    RPCServiceFactory.getInstance().getService().getProjectParcels(
      this.session.getSensorbaseHost(), this.session.getUser(), this.session.getPassword(), 
      new AsyncCallback()
      {
        public void onSuccess(Object result) {
          setUserProjects((ArrayList) result);
        }

        public void onFailure(Throwable caught) {
          if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
            CompositeWidget.log(RPCServiceFactory.PRCERRORNAME, 
              "Cannot invoke user validation on servlet.");
          } 
          else {
            CompositeWidget.log(RPCServiceFactory.PRCERRORNAME, caught.toString());
          }
        }
      });
  }  

  /** 
   * Listens for user resizing of the window.
   * @param width of the adjusted window..
   * @param height of the adjusted window.
   */
  public void onWindowResized(int width, int height) {
    this.row1col2header.setWidth(Integer.toString(width - 114) + Styles.PIXELSUFFIX);
  }
}
